<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/weak/">
<meta property="og:title" content="       weak 弱引用的实现方式 | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       weak 弱引用的实现方式 | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/weak/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>weak 弱引用的实现方式</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-09-11"> 2016-09-11
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <p>对于 runtime 的分析还有很长的路，最近在写 block 系列的同时，也回顾一下之前疏漏的细节知识。这篇文章是关于 weak 的具体实现的学习笔记。</p>
<h2 id="runtime--weak-">runtime 对 __weak 弱引用处理方式</h2>
<p>切入主题，这里笔者使用的 runtime 版本为 <a href="http://opensource.apple.com/tarballs/objc4/">objc4-680.tar.gz</a>。
我在入口文件 main.m 中加入如下代码：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">int</span> <span class="n">main</span><span class="p">(</span><span class="n">int</span> <span class="n">argc</span><span class="p">,</span> <span class="n">const</span> <span class="n">char</span> <span class="o">*</span> <span class="n">argv</span><span class="o">[]</span><span class="p">)</span> <span class="p">{</span>
    <span class="vi">@autoreleasepool</span> <span class="p">{</span>
        <span class="sr">//</span> <span class="n">insert</span> <span class="n">code</span> <span class="n">here</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
        <span class="no">NSObject</span> <span class="o">*</span><span class="nb">p</span> <span class="o">=</span> <span class="o">[[</span><span class="no">NSObject</span> <span class="n">alloc</span><span class="o">]</span> <span class="n">init</span><span class="o">]</span><span class="p">;</span>
        <span class="n">__weak</span> <span class="no">NSObject</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="nb">p</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>单步运行，发现会跳入 <code>NSObject.mm</code> 中的 <code>objc_initWeak()</code> 这个方法。在进行编译过程前，clang 其实对 __weak 做了转换，将声明方式做出了如下调整。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">NSObject</span> <span class="n">objc_initWeak</span><span class="p">(</span><span class="o">&amp;</span><span class="nb">p</span><span class="p">,</span> <span class="err">对象指针</span><span class="p">);</span></code></pre></div>
<p>其中的对象指针，就是代码中的 <code>[[NSObject alloc] init]</code> ，而 p 是我们传入的一个弱引用指针。而对于 <code>objc_initWeak()</code> 方法的实现，在 runtime 中的源码如下：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">id</span> <span class="n">objc_initWeak</span><span class="p">(</span><span class="nb">id</span> <span class="o">*</span><span class="n">location</span><span class="p">,</span> <span class="nb">id</span> <span class="n">newObj</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">查看对象实例是否有效</span>
	<span class="sr">//</span> <span class="err">无效对象直接导致指针释放</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">newObj</span><span class="p">)</span> <span class="p">{</span>
        <span class="o">*</span><span class="n">location</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
        <span class="k">return</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">这里传递了三个</span> <span class="n">bool</span> <span class="err">数值</span>
    <span class="sr">//</span> <span class="err">使用</span> <span class="n">template</span> <span class="err">进行常量参数传递是为了优化性能</span>
    <span class="k">return</span> <span class="n">storeWeak</span><span class="o">&lt;</span><span class="kp">false</span><span class="o">/*</span><span class="n">old</span><span class="o">*/</span><span class="p">,</span> <span class="kp">true</span><span class="o">/*</span><span class="kp">new</span><span class="o">*/</span><span class="p">,</span> <span class="kp">true</span><span class="o">/*</span><span class="n">crash</span><span class="o">*/&gt;</span>
        <span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="p">(</span><span class="n">objc_object</span><span class="o">*</span><span class="p">)</span><span class="n">newObj</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>可以看出，这个函数仅仅是一个深层函数的调用入口，而一般的入口函数中，都会做一些简单的判断（例如 objc_msgSend 中的缓存判断），这里判断了其指针指向的类对象是否有效，无效直接释放，不再往深层调用函数。</p>
<p>需要注意的是，当修改弱引用的变量时，这个方法非线程安全。所以切记选择竞争带来的一些问题。</p>
<p>继续阅读 <code>objc_storeWeak()</code> 的实现：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="ss">HaveOld</span><span class="p">:</span>	 <span class="kp">true</span> <span class="o">-</span> <span class="err">变量有值</span>
<span class="sr">//</span> 			<span class="kp">false</span> <span class="o">-</span> <span class="err">需要被及时清理，当前值可能为</span> <span class="kp">nil</span>
<span class="sr">//</span> <span class="ss">HaveNew</span><span class="p">:</span>	 <span class="kp">true</span> <span class="o">-</span> <span class="err">需要被分配的新值，当前值可能为</span> <span class="kp">nil</span>
<span class="sr">//</span> 			<span class="kp">false</span> <span class="o">-</span> <span class="err">不需要分配新值</span>
<span class="sr">//</span> <span class="ss">CrashIfDeallocating</span><span class="p">:</span> <span class="kp">true</span> <span class="o">-</span> <span class="err">说明</span> <span class="n">newObj</span> <span class="err">已经释放或者</span> <span class="n">newObj</span> <span class="err">不支持弱引用，该过程需要暂停</span>
<span class="sr">//</span> 			<span class="kp">false</span> <span class="o">-</span> <span class="err">用</span> <span class="kp">nil</span> <span class="err">替代存储</span>
<span class="n">template</span> <span class="o">&lt;</span><span class="n">bool</span> <span class="no">HaveOld</span><span class="p">,</span> <span class="n">bool</span> <span class="no">HaveNew</span><span class="p">,</span> <span class="n">bool</span> <span class="no">CrashIfDeallocating</span><span class="o">&gt;</span>
<span class="n">static</span> <span class="nb">id</span> <span class="n">storeWeak</span><span class="p">(</span><span class="nb">id</span> <span class="o">*</span><span class="n">location</span><span class="p">,</span> <span class="n">objc_object</span> <span class="o">*</span><span class="n">newObj</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">该过程用来更新弱引用指针的指向</span>
	<span class="sr">//</span> <span class="err">初始化</span> <span class="n">previouslyInitializedClass</span> <span class="err">指针</span>
    <span class="no">Class</span> <span class="n">previouslyInitializedClass</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="nb">id</span> <span class="n">oldObj</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">声明两个</span> <span class="no">SideTable</span>
    <span class="sr">//</span> <span class="err">①</span> <span class="err">新旧散列创建</span>
    <span class="no">SideTable</span> <span class="o">*</span><span class="n">oldTable</span><span class="p">;</span>
    <span class="no">SideTable</span> <span class="o">*</span><span class="n">newTable</span><span class="p">;</span>
	<span class="sr">//</span> <span class="err">获得新值和旧值的锁存位置（用地址作为唯一标示）</span>
	<span class="sr">//</span> <span class="err">通过地址来建立索引标志，防止桶重复</span>
	<span class="sr">//</span> <span class="err">下面指向的操作会改变旧值</span>
  <span class="k">retry</span><span class="p">:</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">HaveOld</span><span class="p">)</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">更改指针，获得以</span> <span class="n">oldObj</span> <span class="err">为索引所存储的值地址</span>
        <span class="n">oldObj</span> <span class="o">=</span> <span class="o">*</span><span class="n">location</span><span class="p">;</span>
        <span class="n">oldTable</span> <span class="o">=</span> <span class="o">&amp;</span><span class="no">SideTables</span><span class="p">()</span><span class="o">[</span><span class="n">oldObj</span><span class="o">]</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">oldTable</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">HaveNew</span><span class="p">)</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">更改新值指针，获得以</span> <span class="n">newObj</span> <span class="err">为索引所存储的值地址</span>
        <span class="n">newTable</span> <span class="o">=</span> <span class="o">&amp;</span><span class="no">SideTables</span><span class="p">()</span><span class="o">[</span><span class="n">newObj</span><span class="o">]</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">newTable</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="p">}</span>
	<span class="sr">//</span> <span class="err">加锁操作，防止多线程中竞争冲突</span>
    <span class="no">SideTable</span><span class="o">::</span><span class="n">lockTwo</span><span class="o">&lt;</span><span class="no">HaveOld</span><span class="p">,</span> <span class="no">HaveNew</span><span class="o">&gt;</span><span class="p">(</span><span class="n">oldTable</span><span class="p">,</span> <span class="n">newTable</span><span class="p">);</span>
	<span class="sr">//</span> <span class="err">避免线程冲突重处理</span>
	<span class="sr">//</span> <span class="n">location</span> <span class="err">应该与</span> <span class="n">oldObj</span> <span class="err">保持一致，如果不同，说明当前的</span> <span class="n">location</span> <span class="err">已经处理过</span> <span class="n">oldObj</span> <span class="err">可是又被其他线程所修改</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">HaveOld</span>  <span class="o">&amp;&amp;</span>  <span class="o">*</span><span class="n">location</span> <span class="o">!=</span> <span class="n">oldObj</span><span class="p">)</span> <span class="p">{</span>
        <span class="no">SideTable</span><span class="o">::</span><span class="n">unlockTwo</span><span class="o">&lt;</span><span class="no">HaveOld</span><span class="p">,</span> <span class="no">HaveNew</span><span class="o">&gt;</span><span class="p">(</span><span class="n">oldTable</span><span class="p">,</span> <span class="n">newTable</span><span class="p">);</span>
        <span class="n">goto</span> <span class="k">retry</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">防止弱引用间死锁</span>
    <span class="sr">//</span> <span class="err">并且通过</span> <span class="o">+</span><span class="kp">initialize</span> <span class="err">初始化构造器保证所有弱引用的</span> <span class="n">isa</span> <span class="err">非空指向</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">HaveNew</span>  <span class="o">&amp;&amp;</span>  <span class="n">newObj</span><span class="p">)</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">获得新对象的</span> <span class="n">isa</span> <span class="err">指针</span>
        <span class="no">Class</span> <span class="n">cls</span> <span class="o">=</span> <span class="n">newObj</span><span class="o">-&gt;</span><span class="n">getIsa</span><span class="p">();</span>
        <span class="sr">//</span> <span class="err">判断</span> <span class="n">isa</span> <span class="err">非空且已经初始化</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">cls</span> <span class="o">!=</span> <span class="n">previouslyInitializedClass</span>  <span class="o">&amp;&amp;</span>  
            <span class="o">!</span><span class="p">((</span><span class="n">objc_class</span> <span class="o">*</span><span class="p">)</span><span class="n">cls</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isInitialized</span><span class="p">())</span> <span class="p">{</span>
        	<span class="sr">//</span> <span class="err">解锁</span>
            <span class="no">SideTable</span><span class="o">::</span><span class="n">unlockTwo</span><span class="o">&lt;</span><span class="no">HaveOld</span><span class="p">,</span> <span class="no">HaveNew</span><span class="o">&gt;</span><span class="p">(</span><span class="n">oldTable</span><span class="p">,</span> <span class="n">newTable</span><span class="p">);</span>
            <span class="sr">//</span> <span class="err">对其</span> <span class="n">isa</span> <span class="err">指针进行初始化</span>
            <span class="n">_class_initialize</span><span class="p">(</span><span class="n">_class_getNonMetaClass</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="n">newObj</span><span class="p">));</span>
            <span class="sr">//</span> <span class="err">如果该类已经完成执行</span> <span class="o">+</span><span class="kp">initialize</span> <span class="err">方法是最理想情况</span>
            <span class="sr">//</span> <span class="err">如果该类</span> <span class="o">+</span><span class="kp">initialize</span> <span class="err">在线程中</span> 
            <span class="sr">//</span> <span class="err">例如</span> <span class="o">+</span><span class="kp">initialize</span> <span class="err">正在调用</span> <span class="n">storeWeak</span> <span class="err">方法</span>
            <span class="sr">//</span> <span class="err">需要手动对其增加保护策略，并设置</span> <span class="n">previouslyInitializedClass</span> <span class="err">指针进行标记</span>
            <span class="n">previouslyInitializedClass</span> <span class="o">=</span> <span class="n">cls</span><span class="p">;</span>
			<span class="sr">//</span> <span class="err">重新尝试</span>
            <span class="n">goto</span> <span class="k">retry</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">②</span> <span class="err">清除旧值</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">HaveOld</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">weak_unregister_no_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">oldTable</span><span class="o">-&gt;</span><span class="n">weak_table</span><span class="p">,</span> <span class="n">oldObj</span><span class="p">,</span> <span class="n">location</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">③</span> <span class="err">分配新值</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">HaveNew</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">newObj</span> <span class="o">=</span> <span class="p">(</span><span class="n">objc_object</span> <span class="o">*</span><span class="p">)</span><span class="n">weak_register_no_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">newTable</span><span class="o">-&gt;</span><span class="n">weak_table</span><span class="p">,</span> 
                                                      <span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="n">newObj</span><span class="p">,</span> <span class="n">location</span><span class="p">,</span> 
                                                      <span class="no">CrashIfDeallocating</span><span class="p">);</span>
        <span class="sr">//</span> <span class="err">如果弱引用被释放</span> <span class="n">weak_register_no_lock</span> <span class="err">方法返回</span> <span class="kp">nil</span> 
        <span class="sr">//</span> <span class="err">在引用计数表中设置若引用标记位</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">newObj</span>  <span class="o">&amp;&amp;</span>  <span class="o">!</span><span class="n">newObj</span><span class="o">-&gt;</span><span class="n">isTaggedPointer</span><span class="p">())</span> <span class="p">{</span>
        	<span class="sr">//</span> <span class="err">弱引用位初始化操作</span>
			<span class="sr">//</span> <span class="err">引用计数那张散列表的</span><span class="n">weak</span><span class="err">引用对象的引用计数中标识为</span><span class="n">weak</span><span class="err">引用</span>
            <span class="n">newObj</span><span class="o">-&gt;</span><span class="n">setWeaklyReferenced_nolock</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="sr">//</span> <span class="err">之前不要设置</span> <span class="n">location</span> <span class="err">对象，这里需要更改指针指向</span>
        <span class="o">*</span><span class="n">location</span> <span class="o">=</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="n">newObj</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="sr">//</span> <span class="err">没有新值，则无需更改</span>
    <span class="p">}</span>
    <span class="no">SideTable</span><span class="o">::</span><span class="n">unlockTwo</span><span class="o">&lt;</span><span class="no">HaveOld</span><span class="p">,</span> <span class="no">HaveNew</span><span class="o">&gt;</span><span class="p">(</span><span class="n">oldTable</span><span class="p">,</span> <span class="n">newTable</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="n">newObj</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>其中标注的一些要点，开始逐一介绍：</p>
<h2 id="sidetable">引用计数和弱引用依赖表 SideTable</h2>
<p><code>SideTable</code> 这个结构体，我给他起名<strong>引用计数和弱引用依赖表</strong>，因为它主要用于管理对象的引用计数和 <code>weak</code> 表。在 <code>NSObject.mm</code> 中声明其数据结构：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="no">SideTable</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">保证原子操作的自旋锁</span>
    <span class="n">spinlock_t</span> <span class="n">slock</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">引用计数的</span> <span class="nb">hash</span> <span class="err">表</span>
    <span class="no">RefcountMap</span> <span class="n">refcnts</span><span class="p">;</span>
    <span class="sr">//</span> <span class="n">weak</span> <span class="err">引用全局</span> <span class="nb">hash</span> <span class="err">表</span>
    <span class="n">weak_table_t</span> <span class="n">weak_table</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>在之前的 runtime 版本中，有一个较为重要的成员方法，用来根据对象的地址在缓存中取出对应的 <code>SideTable</code> 实例：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">static</span> <span class="no">SideTable</span> <span class="o">*</span><span class="n">tableForPointer</span><span class="p">(</span><span class="n">const</span> <span class="n">void</span> <span class="o">*</span><span class="nb">p</span><span class="p">);</span></code></pre></div>
<p>而在上面 <code>objc_storeWeak</code> 方法中，取出实例的方法变成了 <code>&amp;SideTables()[xxxObj];</code> 这种方式。查看方法的实现，发现了如下函数：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">static</span> <span class="no">StripedMap</span><span class="o">&lt;</span><span class="no">SideTable</span><span class="o">&gt;&amp;</span> <span class="no">SideTables</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">*</span><span class="n">reinterpret_cast</span><span class="o">&lt;</span><span class="no">StripedMap</span><span class="o">&lt;</span><span class="no">SideTable</span><span class="o">&gt;*&gt;</span><span class="p">(</span><span class="no">SideTableBuf</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>在取出实例方法的实现中，使用了 C++ 标准转换运算符 <strong>reinterpret_cast</strong> ，其表达方式为：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">reinterpret_cast</span> <span class="o">&lt;</span><span class="n">new_type</span><span class="o">&gt;</span> <span class="p">(</span><span class="n">expression</span><span class="p">)</span></code></pre></div>
<p>用来处理无关类型之间的转换。该关键字会产生一个新值，并<strong>保证与原参数（expression）拥有完全相同的比特位</strong>。</p>
<p>而 <code>StripedMap</code> 是一个模板类（Template Class），通过传入类（结构体）参数，会动态修改在该类中的一个 <code>array</code> 成员存储的元素类型，并且其中提供了一个针对于地址的 hash 算法，用作存储 key。可以说， <code>StripedMap</code> 提供了一套拥有将地址作为 key 的 hash table 解决方案，而该方案采用了模板类，是拥有泛型性的。</p>
<p>介绍了与对象相关联的 SideTable 检索方式，再来看 SideTable 的成员和作用。</p>
<p>对于 slock 和 refcnts 两个成员不用多说，第一个是为了防止竞争选择的自旋锁，第二个是协助对象的 isa 指针的 <code>extra_rc</code> 共同引用计数的变量（对于对象结果，在今后的文中提到）。这里主要看 <code>weak</code> 全局 hash 表的结构与作用。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">weak_table_t</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">保存了所有指向指定对象的</span> <span class="n">weak</span> <span class="err">指针</span>
    <span class="n">weak_entry_t</span> <span class="o">*</span><span class="n">weak_entries</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">存储空间</span>
    <span class="n">size_t</span>    <span class="n">num_entries</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">参与判断引用计数辅助量</span>
    <span class="n">uintptr_t</span> <span class="n">mask</span><span class="p">;</span>
    <span class="sr">//</span> <span class="nb">hash</span> <span class="n">key</span> <span class="err">最大偏移值</span>
    <span class="n">uintptr_t</span> <span class="n">max_hash_displacement</span><span class="p">;</span>
<span class="p">};</span></code></pre></div>
<p>这是一个全局弱引用表。使用不定类型对象的地址作为 key ，用 weak_entry_t 类型结构体对象作为 value 。其中的 weak_entries 成员，从字面意思上看，即为弱引用表入口。其实现也是这样的。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">typedef</span> <span class="n">objc_object</span> <span class="o">**</span> <span class="n">weak_referrer_t</span><span class="p">;</span>
<span class="n">struct</span> <span class="n">weak_entry_t</span> <span class="p">{</span>
    <span class="no">DisguisedPtr</span><span class="o">&lt;</span><span class="n">objc_object</span><span class="o">&gt;</span> <span class="n">referent</span><span class="p">;</span>
    <span class="n">union</span> <span class="p">{</span>
        <span class="n">struct</span> <span class="p">{</span>
            <span class="n">weak_referrer_t</span> <span class="o">*</span><span class="n">referrers</span><span class="p">;</span>
            <span class="n">uintptr_t</span>        <span class="n">out_of_line</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
            <span class="n">uintptr_t</span>        <span class="n">num_refs</span> <span class="p">:</span> <span class="no">PTR_MINUS_1</span><span class="p">;</span>
            <span class="n">uintptr_t</span>        <span class="n">mask</span><span class="p">;</span>
            <span class="n">uintptr_t</span>        <span class="n">max_hash_displacement</span><span class="p">;</span>
        <span class="p">};</span>
        <span class="n">struct</span> <span class="p">{</span>
            <span class="sr">//</span> <span class="n">out_of_line</span><span class="o">=</span><span class="mi">0</span> <span class="n">is</span> <span class="no">LSB</span> <span class="n">of</span> <span class="n">one</span> <span class="n">of</span> <span class="n">these</span> <span class="p">(</span><span class="n">don</span><span class="err">&#39;</span><span class="n">t</span> <span class="n">care</span> <span class="n">which</span><span class="p">)</span>
            <span class="n">weak_referrer_t</span>  <span class="n">inline_referrers</span><span class="o">[</span><span class="no">WEAK_INLINE_COUNT</span><span class="o">]</span><span class="p">;</span>
        <span class="p">};</span>
 <span class="p">}</span></code></pre></div>
<p>在 weak_entry_t 的结构中，<code>DisguisedPtr&lt;objc_object&gt; referent</code> 是对泛型对象的指针做了一个封装，通过这个泛型类来解决内存泄漏的问题。从注释中写 <code>out_of_line</code> 成员为最低有效位，当其为0的时候， <code>weak_referrer_t</code> 成员将扩展为多行静态 hash table。其实其中的 <code>weak_referrer_t</code> 是二维 <code>objc_object</code> 的别名，通过一个二维指针地址偏移，用下标作为 hash 的 key，做成了一个弱引用散列。</p>
<p>那么在有效位未生效的时候，<code>out_of_line</code> 、 <code>num_refs</code>、 <code>mask</code> 、 <code>max_hash_displacement</code> 有什么作用？以下是笔者自身的猜测：</p>
<ul>
  <li><strong>out_of_line</strong>：最低有效位，也是标志位。当标志位 0 时，增加引用表指针纬度。</li>
  <li><strong>num_refs</strong>：引用数值。这里记录弱引用表中引用有效数字，因为弱引用表使用的是静态 hash 结构，所以需要使用变量来记录数目。</li>
  <li><strong>mask</strong>：计数辅助量。</li>
  <li><strong>max_hash_displacement</strong>：hash 元素上限阀值。</li>
</ul>
<p>其实 out_of_line 的值通常情况下是等于零的，所以弱引用表总是一个 objc_objective 指针二维数组。一维 objc_objective 指针可构成一张弱引用散列表，通过第三纬度实现了多张散列表，并且表数量为 WEAK_INLINE_COUNT 。</p>
<p>总结一下 <code>StripedMap&lt;SideTable&gt;[]</code> ： <code>StripedMap</code> 是一个模板类，在这个类中有一个 array 成员，用来存储 PaddedT 对象，并且其中对于 <code>[]</code> 符的重载定义中，会返回这个 PaddedT 的 value 成员，这个 value 就是我们传入的 T 泛型成员，也就是 SideTable 对象。在 array 的下标中，这里使用了 indexForPointer 方法通过位运算计算下标，实现了静态的 Hash Table。而在 weak_table 中，其成员 weak_entry 会将传入对象的地址加以封装起来，并且其中也有访问全局弱引用表的入口。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/sidetable.png" alt="" /></p>
<h2 id="weakunregisternolock">旧对象解除注册操作 weak_unregister_no_lock</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#define WEAK_INLINE_COUNT 4</span>
<span class="n">void</span> <span class="n">weak_unregister_no_lock</span><span class="p">(</span><span class="n">weak_table_t</span> <span class="o">*</span><span class="n">weak_table</span><span class="p">,</span> <span class="nb">id</span> <span class="n">referent_id</span><span class="p">,</span> 
                        <span class="nb">id</span> <span class="o">*</span><span class="n">referrer_id</span><span class="p">)</span> <span class="p">{</span>
    <span class="sr">//</span> <span class="err">在入口方法中，传入了</span> <span class="n">weak_table</span> <span class="err">弱引用表，</span><span class="n">referent_id</span> <span class="err">旧对象以及</span> <span class="n">referent_id</span> <span class="err">旧对象对应的地址</span>
    <span class="sr">//</span> <span class="err">用指针去访问</span> <span class="n">oldObj</span> <span class="err">和</span> <span class="o">*</span><span class="n">location</span>  
    <span class="n">objc_object</span> <span class="o">*</span><span class="n">referent</span> <span class="o">=</span> <span class="p">(</span><span class="n">objc_object</span> <span class="o">*</span><span class="p">)</span><span class="n">referent_id</span><span class="p">;</span>
    <span class="n">objc_object</span> <span class="o">**</span><span class="n">referrer</span> <span class="o">=</span> <span class="p">(</span><span class="n">objc_object</span> <span class="o">**</span><span class="p">)</span><span class="n">referrer_id</span><span class="p">;</span>
    <span class="n">weak_entry_t</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
	<span class="sr">//</span> <span class="err">如果其对象为</span> <span class="kp">nil</span><span class="err">，无需取消注册</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">referent</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
	<span class="sr">//</span> <span class="n">weak_entry_for_referent</span> <span class="err">根据首对象查找</span> <span class="n">weak_entry</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">entry</span> <span class="o">=</span> <span class="n">weak_entry_for_referent</span><span class="p">(</span><span class="n">weak_table</span><span class="p">,</span> <span class="n">referent</span><span class="p">)))</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">通过地址来解除引用关联</span>	
        <span class="n">remove_referrer</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="n">referrer</span><span class="p">);</span>
        <span class="n">bool</span> <span class="n">empty</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">检测</span> <span class="n">out_of_line</span> <span class="err">位的情况</span>
        <span class="sr">//</span> <span class="err">检测</span> <span class="n">num_refs</span> <span class="err">位的情况</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">out_of_line</span>  <span class="o">&amp;&amp;</span>  <span class="n">entry</span><span class="o">-&gt;</span><span class="n">num_refs</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">empty</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
        	<span class="sr">//</span> <span class="err">将引用表中记录为空</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="no">WEAK_INLINE_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">inline_referrers</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">empty</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span> 
                    <span class="k">break</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
	<span class="sr">//</span> <span class="err">从弱引用的</span> <span class="n">zone</span> <span class="err">表中删除</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">empty</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">weak_entry_remove</span><span class="p">(</span><span class="n">weak_table</span><span class="p">,</span> <span class="n">entry</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">这里不会设置</span> <span class="o">*</span><span class="n">referrer</span> <span class="o">=</span> <span class="kp">nil</span><span class="err">，因为</span> <span class="n">objc_storeWeak</span><span class="p">()</span> <span class="err">函数会需要该指针</span>
<span class="p">}</span></code></pre></div>
<p>该方法主要作用是将旧对象在 weak_table 中接触 weak 指针的对应绑定。根据函数名，称之为解除注册操作。从源码中，可以知道其功能就是从 weak_table 中接触 weak 指针的绑定。而其中的遍历查询，就是针对于 weak_entry 中的多张弱引用散列表。</p>
<h2 id="weakregisternolock">新对象添加注册操作 weak_register_no_lock</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">id</span> <span class="n">weak_register_no_lock</span><span class="p">(</span><span class="n">weak_table_t</span> <span class="o">*</span><span class="n">weak_table</span><span class="p">,</span> <span class="nb">id</span> <span class="n">referent_id</span><span class="p">,</span>
                      <span class="nb">id</span> <span class="o">*</span><span class="n">referrer_id</span><span class="p">,</span> <span class="n">bool</span> <span class="n">crashIfDeallocating</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">在入口方法中，传入了</span> <span class="n">weak_table</span> <span class="err">弱引用表，</span><span class="n">referent_id</span> <span class="err">旧对象以及</span> <span class="n">referent_id</span> <span class="err">旧对象对应的地址</span>
    <span class="sr">//</span> <span class="err">用指针去访问</span> <span class="n">oldObj</span> <span class="err">和</span> <span class="o">*</span><span class="n">location</span>
    <span class="n">objc_object</span> <span class="o">*</span><span class="n">referent</span> <span class="o">=</span> <span class="p">(</span><span class="n">objc_object</span> <span class="o">*</span><span class="p">)</span><span class="n">referent_id</span><span class="p">;</span>
    <span class="n">objc_object</span> <span class="o">**</span><span class="n">referrer</span> <span class="o">=</span> <span class="p">(</span><span class="n">objc_object</span> <span class="o">**</span><span class="p">)</span><span class="n">referrer_id</span><span class="p">;</span>
	<span class="sr">//</span> <span class="err">检测对象是否生效、以及是否使用了</span> <span class="n">tagged</span> <span class="n">pointer</span> <span class="err">技术</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">referent</span>  <span class="o">||</span>  <span class="n">referent</span><span class="o">-&gt;</span><span class="n">isTaggedPointer</span><span class="p">())</span> <span class="k">return</span> <span class="n">referent_id</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">保证引用对象是否有效</span>
    <span class="sr">//</span> <span class="n">hasCustomRR</span> <span class="err">方法检查类（包括其父类）中是否含有默认的方法</span>
    <span class="n">bool</span> <span class="n">deallocating</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">referent</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">hasCustomRR</span><span class="p">())</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">检查</span> <span class="n">dealloc</span> <span class="err">状态</span>
        <span class="n">deallocating</span> <span class="o">=</span> <span class="n">referent</span><span class="o">-&gt;</span><span class="n">rootIsDeallocating</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">会返回</span> <span class="n">referent</span> <span class="err">的</span> <span class="no">SEL_allowsWeakReference</span> <span class="err">方法的地址</span>
        <span class="no">BOOL</span> <span class="p">(</span><span class="o">*</span><span class="n">allowsWeakReference</span><span class="p">)(</span><span class="n">objc_object</span> <span class="o">*</span><span class="p">,</span> <span class="no">SEL</span><span class="p">)</span> <span class="o">=</span> 
            <span class="p">(</span><span class="no">BOOL</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="n">objc_object</span> <span class="o">*</span><span class="p">,</span> <span class="no">SEL</span><span class="p">))</span>
            <span class="n">object_getMethodImplementation</span><span class="p">((</span><span class="nb">id</span><span class="p">)</span><span class="n">referent</span><span class="p">,</span> 
                                           <span class="no">SEL_allowsWeakReference</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">((</span><span class="no">IMP</span><span class="p">)</span><span class="n">allowsWeakReference</span> <span class="o">==</span> <span class="n">_objc_msgForward</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kp">nil</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="n">deallocating</span> <span class="o">=</span>
            <span class="o">!</span> <span class="p">(</span><span class="o">*</span><span class="n">allowsWeakReference</span><span class="p">)(</span><span class="n">referent</span><span class="p">,</span> <span class="no">SEL_allowsWeakReference</span><span class="p">);</span>
    <span class="p">}</span>
	<span class="sr">//</span> <span class="err">由于</span> <span class="n">dealloc</span> <span class="err">导致</span> <span class="n">crash</span> <span class="err">，并输出日志</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">deallocating</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">crashIfDeallocating</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">_objc_fatal</span><span class="p">(</span><span class="s2">&quot;Cannot form weak reference to instance (%p) of &quot;</span>
                        <span class="s2">&quot;class %s. It is possible that this object was &quot;</span>
                        <span class="s2">&quot;over-released, or is in the process of deallocation.&quot;</span><span class="p">,</span>
                        <span class="p">(</span><span class="n">void</span><span class="o">*</span><span class="p">)</span><span class="n">referent</span><span class="p">,</span> <span class="n">object_getClassName</span><span class="p">((</span><span class="nb">id</span><span class="p">)</span><span class="n">referent</span><span class="p">));</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kp">nil</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">记录并存储对应引用表</span> <span class="n">weak_entry</span>
    <span class="n">weak_entry_t</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">对于给定的弱引用查询</span> <span class="n">weak_table</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">entry</span> <span class="o">=</span> <span class="n">weak_entry_for_referent</span><span class="p">(</span><span class="n">weak_table</span><span class="p">,</span> <span class="n">referent</span><span class="p">)))</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">增加弱引用表于附加对象上</span>
        <span class="n">append_referrer</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="n">referrer</span><span class="p">);</span>
    <span class="p">}</span> 
    <span class="k">else</span> <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">自行创建弱引用表</span>
        <span class="n">weak_entry_t</span> <span class="n">new_entry</span><span class="p">;</span>
        <span class="n">new_entry</span><span class="o">.</span><span class="n">referent</span> <span class="o">=</span> <span class="n">referent</span><span class="p">;</span>
        <span class="n">new_entry</span><span class="o">.</span><span class="n">out_of_line</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">new_entry</span><span class="o">.</span><span class="n">inline_referrers</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span> <span class="n">referrer</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="no">WEAK_INLINE_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">new_entry</span><span class="o">.</span><span class="n">inline_referrers</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="sr">//</span> <span class="err">如果给定的弱引用表满容，进行自增长</span>
        <span class="n">weak_grow_maybe</span><span class="p">(</span><span class="n">weak_table</span><span class="p">);</span>
        <span class="sr">//</span> <span class="err">向对象添加弱引用表关联，不进行检查直接修改指针指向</span>
        <span class="n">weak_entry_insert</span><span class="p">(</span><span class="n">weak_table</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">new_entry</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="err">这里不会设置</span> <span class="o">*</span><span class="n">referrer</span> <span class="o">=</span> <span class="kp">nil</span><span class="err">，因为</span> <span class="n">objc_storeWeak</span><span class="p">()</span> <span class="err">函数会需要该指针</span>
    <span class="k">return</span> <span class="n">referent_id</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>这一步与上一步相反，通过 weak_register_no_lock 函数把心的对象进行注册操作，完成与对应的弱引用表进行绑定操作。</p>
<h2 id="section">初始化弱引用对象流程一览</h2>
<p>弱引用的初始化，从上文的分析中可以看出，主要的操作部分就在弱引用表的取键、查询散列、创建弱引用表等操作，可以总结出如下的流程图：</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/weaktable-2.png" alt="weaktable-2" /></p>
<p>这个图中省略了很多情况的判断，但是当声明一个 <code>__weak</code> 会调用上图中的这些方法。当然， <code>storeWeak</code> 方法不仅仅用在 <code>__weak</code> 的声明中，在 class 内部的操作中也会常常通过该方法来对 weak 对象进行操作。</p>
<p>以上就是对于 weak 弱引用对象的初始化时 runtime 内部的执行过程，想必阅读后会对其结构有更深的理解。</p>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《weak 弱引用的实现方式》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/weak/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/weak/&amp;title=《weak 弱引用的实现方式》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/weak/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/weak/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>